package cn.xy.ymcc.service.impl;

import cn.xy.ymcc.constants.CommonConstants;
import cn.xy.ymcc.constants.GlobalError;
import cn.xy.ymcc.constants.SmsCodeConstants;
import cn.xy.ymcc.constants.YmccError;
import cn.xy.ymcc.domain.MessageSms;
import cn.xy.ymcc.dto.RedisRegisterDto;
import cn.xy.ymcc.dto.RegisterDto;
import cn.xy.ymcc.exception.GlobalException;
import cn.xy.ymcc.service.IMessageSmsService;
import cn.xy.ymcc.service.IVerifyCodeService;
import cn.xy.ymcc.util.AssertUtil;
import cn.xy.ymcc.util.StrUtils;
import cn.xy.ymcc.util.VerifyCodeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private IMessageSmsService messageSmsService;

    @Autowired
    private SmsCodeConstants smsCodeConstants;

    @Override
    public String imageCode(String key) {
        //先判断前端传来的key值不为空，为空抛异常
        if(StringUtils.isEmpty(key)){
            throw new GlobalException(YmccError.NO_EMPTY_EMP_NAME.getMessage(),YmccError.NO_EMPTY_EMP_NAME.getCode());
        }
        //生成验证码,设置验证码长度为4.
        String code = StrUtils.getComplexRandomString(4);
        //将验证码和前端的key传入redis中，设置保存时间，
        redisTemplate.opsForValue().set(key,code,5, TimeUnit.MINUTES);
        //使用2D引擎把code转为图片，再将图使用Base64转换为字符串并返回，
        return VerifyCodeUtils.VerifyCode(100,40,code);
    }

    /**
     * 获取短信验证码
     * @param dto
     */
    @Override
    public void smsCode(RegisterDto dto) {
        //获取手机号，图片验证码，图片验证码的key
        String imageCodeValue = dto.getImageCode();
        String phone = dto.getMobile();
        String imageCodeKey = dto.getImageCodeKey();

        //进行校验
//        if(StringUtils.isEmpty(imageCodeKey)||StringUtils.isEmpty(phone)||StringUtils.isEmpty(imageCodeValue)){
//            throw new GlobalException(YmccError.COMMON_INFORMATION_NO_EMPTY.getMessage(),YmccError.COMMON_INFORMATION_NO_EMPTY.getCode());
//        }
        //断言优化
        AssertUtil.isNotEmpty(imageCodeKey,YmccError.COMMON_INFORMATION_NO_EMPTY.getMessage());
        AssertUtil.isNotEmpty(phone,YmccError.COMMON_INFORMATION_NO_EMPTY.getMessage());
        AssertUtil.isNotEmpty(imageCodeValue,YmccError.COMMON_INFORMATION_NO_EMPTY.getMessage());
        AssertUtil.isPhone(phone,YmccError.COMMON_PHONE_ILLEGAL.getMessage());//校验手机号合法与否

        //校验图形验证码，
        //从redis中获取key对应的value
        Object imageCodeTmp = redisTemplate.opsForValue().get(imageCodeKey);

        //判断获取的结果是否为null，
//        if(imageCodeTmp == null){
//            //为空则表示redis缓存中图形验证码已过期被销毁，返回前端异常。
//            throw new GlobalException(YmccError.NO_EMPTY_EMP_NAME.getMessage(),YmccError.NO_EMPTY_EMP_NAME.getCode());
//        }
        AssertUtil.isNotNull(imageCodeTmp,YmccError.NO_EMPTY_EMP_NAME.getMessage());


        //获取到图形验证码，还要判断是否正确
        if(!imageCodeTmp.toString().equalsIgnoreCase(imageCodeValue)){
            //不正确就抛异常
            throw new GlobalException(YmccError.NO_EMPTY_EMP_NAME.getMessage(),YmccError.NO_EMPTY_EMP_NAME.getCode());
        }



        //图形验证码没问题就要验证手机号了
        //如果没有注册过，则在user表中不会存在此手机号，所以用此判断。
        //使用loadByPhone查询user表，如果为空，则说明没注册过。
//        User user = userMapper.loadByPhone(phone);
//        //不为空，则说明注册过了，直接让他去登录
//        if(user!=null){
//            throw new BusinessException("此手机号已注册，请直接登录");
//        }

        //使用register:手机号，这种方式拼接key，用以判断redis中是否存在对应的value
        String key = String.format(CommonConstants.REGISTER_PHONE_PREFIX,phone);
        //使用拼接的key去获取redis中的验证码，验证码不一定存在，取决于下面的判断。
        Object smsCodeTmp = redisTemplate.opsForValue().get(key);
        //创建空字符串用于设置验证码
        String smsCode = null;
        //判断smsCodeTmp是否为空
        if(smsCodeTmp!=null){

            RedisRegisterDto registerDto = (RedisRegisterDto) smsCodeTmp;
            //不为空，说明redis中还有对应的验证码
            //判断验证码是否过了一分钟重发事件，添加此判断的意义在于防止不法分子恶意攻击
            //生成value的格式在smsCodeTmp判断为空时设置。格式为“验证码”：“时间戳”。
            //通过:分隔，选取下标为1的元素，也就是时间戳。
            Long oldTime = Long.valueOf(smsCodeTmp.toString().split(":")[1]);
            //通过当前时间减去时间戳，判断时间是否超过一分钟，如果没有超过，则说明不法分子恶意攻击
//            if(System.currentTimeMillis()-oldTime/1000 <= smsCodeConstants.getInterval()){
//                throw new GlobalException(YmccError.COMMON_SERIOUS.getMessage(),YmccError.COMMON_SERIOUS.getCode());
//            }
            //时间大于一分钟，小于三分钟，则说明验证码还有效，就使用当前验证码，并且刷新时间戳。
            //断言优化
            boolean canSend = System.currentTimeMillis() - oldTime / 1000 > smsCodeConstants.getInterval();
            AssertUtil.isTrue(canSend,YmccError.COMMON_SERIOUS.getMessage());

            smsCode =registerDto.getSmsCode();
        }else {
            //为空，则说明没有对应的value值
            //生成一个新的验证码,指定长度
            smsCode = StrUtils.getRandomString(smsCodeConstants.getSize());
        }
        //将验证码和key存进redis，并且将验证码拼接时间戳，用于计算过期时间
        RedisRegisterDto redisRegisterDto = new RedisRegisterDto(smsCode, System.currentTimeMillis());
        //存进redis
        redisTemplate.opsForValue().set(key,redisRegisterDto,smsCodeConstants.getTimeout(),TimeUnit.SECONDS);
//        SmsUtil.sendSms(phone,"亲，您的验证码是："+smsCode+"。请在3分钟内使用！！");
        String message = String.format(smsCodeConstants.getSmsmessage(), smsCode);

        System.out.println(message);
        //存储发送信息到数据库
        saveMessageSms(smsCode);
    }

    private void saveMessageSms(String smsCode) {
        MessageSms messageSms = new MessageSms();
        messageSms.setContent(smsCode);
        messageSms.setSendTime(new Date());
        messageSms.setTitle(smsCodeConstants.getTitle());
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        messageSms.setIp(request.getRemoteAddr());
        messageSmsService.insert(messageSms);
    }


}












